import java.sql.*;
import java.util.*;

/**
 * 
 * Class for DB Connections Pool
 *
 */
public class DBConnectionPool {
	
	private String 	URI;
	private String 	user; 
	private String 	pass;
	private String 	drivername; 
	private int 	maxconn;
	private static DBConnectionPool pool;
	private ArrayList<Connection> freeConnections;
	
	
	public DBConnectionPool() {
		
	}
	
	
	private DBConnectionPool(String uri, String user, String pass,
			String drivername, int maxconn) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
		super();
		URI = uri; 
		this.user = user;
		this.pass = pass;
		this.drivername = drivername;
		this.maxconn = maxconn;
		freeConnections = new ArrayList<Connection>(maxconn);
		loadJDBCDriver();
	}

	/**
	 * Loading the JDBC driver and register it
	 */
	private void loadJDBCDriver() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
			Driver driver = (Driver)Class.forName(this.drivername).newInstance();
			DriverManager.registerDriver(driver);
	}
	
	
	public static synchronized DBConnectionPool getInstance(String uri, 
															String user, 
															String pass, 
															String drivername, 
															int maxconn) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
		if (pool == null) {
			pool = new DBConnectionPool(uri, user, pass, 
										drivername, maxconn);

			// Check if connection was successful, if not return null
			Connection con=null;
			try {
				con = pool.getConnection();
			} catch(SQLException e){
				pool.returnConnection(con);
			}
			if(con!=null) {
				pool.returnConnection(con);
			}
			else {
				pool=null;
			}
		}
		return pool;
	}
	
	
	/**
	 * Gets a valid connection to the database
	 * @return the connection
	 * @throws SQLException 
	 */
	public synchronized Connection getConnection() throws SQLException {
		Connection rescon = null;
		logger mylogger=GUI.getLogger();
		if (!freeConnections.isEmpty()) {
			rescon = freeConnections.get(freeConnections.size()-1);
			freeConnections.remove(rescon);
			try {
				if (rescon.isClosed()) {
					if (mylogger!=null) mylogger.log(2,"Removed closed connection!");
					// Try again recursively
					rescon = getConnection();
				}
			} catch (SQLException e) {
				if (mylogger!=null) mylogger.log(2,"Removed closed connection!");
				// Try again recursively
				rescon = getConnection();
			} catch (Exception e) {
				if (mylogger!=null) mylogger.log(2,"Removed closed connection!");
				// Try again recursively
				rescon = getConnection();
			}
		} else {
			rescon = createConnection();
		}
		return rescon;
	}
	
	/**
	 * Creates a new connection 
	 * @return The created connection or null
	 * @throws SQLException 
	 */
	private Connection createConnection() throws SQLException {
		Connection rescon = null;
		logger mylogger=GUI.getLogger();
		try {
			if (user == null) {
				rescon = DriverManager.getConnection(URI);
			} else {
				rescon = DriverManager.getConnection(URI, user, pass);
			}
			// new connection in connection pool created
		} catch (SQLException e) {
			if (mylogger!=null) mylogger.log(2,"cannot create a new connection");
			rescon = null;
		}
		return rescon;
	}
	
	/**
	 * Returns a connection back to the connection pool
	 * @param con - The connection to be returned to the pool
	 */
	public synchronized void returnConnection(Connection con) {
		if ((con != null) 
			&&
			(freeConnections.size() <= maxconn)) {
			freeConnections.add(con);
		}
	}
	
	/**
	 * Iterates and releasing the connections 
	 */
	public synchronized void release() {
		logger mylogger=GUI.getLogger();
		for(Connection con : freeConnections) {
			try {
				con.close();
			} catch (SQLException e) {
				if (mylogger!=null) mylogger.log(2,"Cannot close connection! (Probably already closed?)");
			}
		}
		freeConnections.clear();
	}
}
